import router from '@/router/index'
import Cookies from 'js-cookie'

interface FetchUtilOptions {
  baseURL: string
  interceptor?: FetchInterceptor
}

export interface Resp<T> {
  code: number
  message: string
  data?: T
}

interface FetchInterceptor {
  request?: (options?: RequestInit) => RequestInit | undefined
}

export class FetchUtil {
  baseURL: string
  interceptor: FetchInterceptor
  constructor(options: FetchUtilOptions) {
    this.baseURL = (options.baseURL ? options.baseURL : '') + '/'
    if (options.interceptor) {
      this.interceptor = options.interceptor
    } else {
      this.interceptor = {
        request: undefined
      }
    }
  }
  static createFetch(options: FetchUtilOptions) {
    return new FetchUtil(options)
  }

  static objectToQueryParams(obj: { [key: string]: any }): string {
    const queryParams: string[] = []

    for (const key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        const value = obj[key]
        if (Array.isArray(value)) {
          // 如果值是数组，则对每个元素进行编码
          value.forEach((val) => {
            queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`)
          })
        } else {
          // 否则，对值进行编码
          queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        }
      }
    }

    return queryParams.join('&')
  }

  $fetch<T>(url: string, options?: RequestInit): Promise<Resp<T>> {
    // http或https开头, 直接访问
    if (!/^(http|https):\/\//.test(url)) {
      // 拼接baseURL
      url = (this.baseURL + url).replace(/\/\//g, '/')
    }
    // 请求拦截器
    if (this.interceptor.request) {
      options = this.interceptor.request(options)
    }
    return fetch(url, options).then((res) => {
      return new Promise<Resp<T>>((resolve, reject) => {
        // 处理响应
        if (res.ok) {
          res.json().then((data) => {
            if (data.code === 401) {
              router.push({
                path: '/login',
                query: {
                  redirect: window.location.pathname
                }
              })
              ElMessage({
                message: '登录状态过期, 请重新登录',
                duration: 0,
                type: 'info',
                grouping: true
              })
            } else if (data.code !== 200) {
              ElMessage({
                message: data.message,
                type: 'error'
              })
            }
            resolve(data)
          })
        } else {
          ElMessage({
            message: '接口请求失败',
            type: 'error'
          })
          reject(res)
        }
      })
    })
  }

  $any<T>(url: string, method: string, body?: object, query?: object): Promise<Resp<T>> {
    // 拼接查询参数
    if (query) {
      const queryParams = FetchUtil.objectToQueryParams(query)
      url += `?${queryParams}`
    }
    const json = JSON.stringify(body)
    return this.$fetch<T>(url, {
      method: method,
      headers: {
        'Content-Type': 'application/json'
      },
      body: json ? json : ''
    })
  }

  $get<T>(url: string, query?: object): Promise<Resp<T>> {
    // 拼接查询参数
    if (query) {
      const queryParams = FetchUtil.objectToQueryParams(query)
      url += `?${queryParams}`
    }
    return this.$fetch<T>(url, {
      method: 'GET'
    })
  }

  $post<T>(url: string, body?: object, query?: object): Promise<Resp<T>> {
    return this.$any(url, 'POST', body, query)
  }

  $put<T>(url: string, body?: object, query?: object): Promise<Resp<T>> {
    return this.$any(url, 'PUT', body, query)
  }

  $delete<T>(url: string, body?: object, query?: object): Promise<Resp<T>> {
    return this.$any(url, 'DELETE', body, query)
  }

  // post 方式下载
  $download(url: string, filename: string, body?: object, query?: object) {
    // 拼接查询参数
    if (query) {
      const queryParams = FetchUtil.objectToQueryParams(query)
      url += `?${queryParams}`
    }
    const json = JSON.stringify(body)
    let options: RequestInit = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: json ? json : ''
    }
    // http或https开头, 直接访问
    if (!/^(http|https):\/\//.test(url)) {
      // 拼接baseURL
      url = (this.baseURL + url).replace(/\/\//g, '/')
    }
    // 请求拦截器
    if (this.interceptor.request) {
      options = this.interceptor.request(options) as RequestInit
    }
    return fetch(url, options).then((res) => {
      const contentType = res.headers.get('Content-Type')
      if (contentType && contentType.indexOf('application/json') !== -1) {
        res.json().then((data) => {
          alert(data.message)
        })
        return
      }
      res.blob().then((blob) => {
        const link = window.document.createElement('a')
        const url = window.URL.createObjectURL(blob)
        link.href = url
        link.download = filename
        window.document.body.appendChild(link)
        link.click()
        link.remove()
        window.URL.revokeObjectURL(url)
      })
    })
  }

  // 文件上传
  $uploadFile(
    url: string,
    fileParamName: string,
    files: File[],
    param: Record<string, any>
  ): Promise<any> {
    // http或https开头, 直接访问
    if (!/^(http|https):\/\//.test(url)) {
      // 拼接baseURL
      url = (import.meta.env.VITE_API_BASE_URL + url).replace(/\/\//g, '/')
    }
    if (!files || files.length === 0) {
      ElMessage({
        message: '文件为空',
        type: 'error'
      })
    }
    const formData = new FormData()
    for (let i = 0; i < files.length; i++) {
      formData.append(fileParamName, files[i])
    }
    for (const key in param) {
      if (Object.prototype.hasOwnProperty.call(param, key)) {
        formData.append(key, param[key])
      }
    }
    return fetch(url, {
      method: 'POST',
      body: formData,
      headers: {
        ContentType: 'multipart/form-data',
        Authorization: Cookies.get('token') || ''
      }
    })
      .then((data) => {
        data.json().then((res) => {
          if (res.code === 200) {
            ElMessage({
              message: '上传成功',
              type: 'success'
            })
          } else {
            ElMessage({
              message: res.message,
              type: 'error'
            })
          }
        })
      })
      .catch((error) => {
        ElMessage({
          message: '失败' + error,
          type: 'error'
        })
      })
  }
}

export const Req = FetchUtil.createFetch({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  // 自定义拦截器
  interceptor: {
    request: (options?: RequestInit) => {
      if (!options) {
        options = {
          headers: {
            Authorization: Cookies.get('token') || ''
          }
        }
      } else {
        options.headers = {
          ...options.headers,
          Authorization: Cookies.get('token') || ''
        }
      }
      return options
    }
  }
})
